#pragma once
#include <string>
#include <map>

namespace html { namespace dom
{
  using namespace std;

  /// @brief symbol that describes a string symbol by a unique integer identifier
  class symbol {
    friend class symbol_table;
    /// @brief id type is a string pointer
    typedef string* id;
  public:
    /// @brief returns a symbol that is "not in list" and that hasn't a matching 
    /// string identifier
    static const id nil() { return NULL; }
    /// @brief standard constructor initializes with symbol::nil()
    symbol() : id_(nil()) {}
    /// @brief constructor that gets a string identifier which will be translated 
    /// to a symbol identifier
    symbol(const string& _name);
    /// @brief constructor that gets a string identifier which will be translated 
    /// to a symbol identifier
    symbol(const char* _name);
    /// @brief constructor that gets an integer identifier 
    symbol(id _id): id_(_id) {}
    /// @brief returns the string identifier that matches the current symbol identifier
    operator const string& () const { return to_str(); }
    /// @brief returns the string identifier that matches the current symbol identifier
    const string& to_str() const { return *id_; }
    /// @brief returns the stored symbol identifier
    operator id () const { return id_; }
    symbol operator|=( symbol _id ) { 
      id_ = (id_!=symbol::nil())?id_:_id;
      return id_;
    }
  private:
    /// @brief stored symbol identifier
    id id_;
  };

  /// @brief table of string identifiers -> symbol identifiers
  class symbol_table {
    /// @brief typedef for the symbol table's basic data structure
    typedef map<string, symbol::id> str2id;
  public:
    /// @brief destructor destroying all stored symbols
    ~symbol_table() {
      for (str2id::iterator it=str_to_id_.begin(); it!=str_to_id_.end(); it++ )
        delete it->second;
    }
    /// @brief translate a symbol id to a string identifier
    symbol operator[] (symbol::id _id) const { return _id; }
    /// @brief translate a string identifier to a symbol id or create a new
    /// entry in the symbol tabel if not exist
    /// @todo known problem: this is not thread safe!
    symbol operator[] (const string & _str) {
      return getadd(_str);
    }
    static symbol getadd( const string& _str );
    static symbol find( const string& _str );
    static symbol_table& get_symbol_table() {
      static symbol_table st;
      return st;
    }
  private:
    /// @brief maps string identifier -> symbol identifier
    str2id         str_to_id_;
  };
  /// @brief global symbol table
  /// @todo may be it's better to get rid of this singleton or synchronize 
  extern symbol_table symbols;
  /// @brief basic symbol 'id' for id attributes
  extern symbol id_symbol;
  /// @brief basic symbol 'class' for class attributes
  extern symbol class_symbol;
  extern symbol comment_symbol;
  extern symbol comment_symbol;

  inline symbol::symbol(const string& _name) { *this = symbol_table::getadd(_name); }
  inline symbol::symbol(const char* _name) { *this = symbol_table::getadd(_name); }
} }
